package com.maoxs.conf;

import com.maoxs.cache.CollectionSerializer;
import com.maoxs.cache.ShiroRedisCacheManager;
import com.maoxs.cache.ShiroRedisSessionDao;
import com.maoxs.realm.CustomRealm;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 安全管理器
     *
     * @return
     */
    @Bean
//    @DependsOn({"customRealm"})
    public DefaultWebSecurityManager securityManager(RedisTemplate redisTemplate, RedisCacheManager redisCacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(customRealm(redisCacheManager));
        securityManager.setCacheManager(shiroRedisCacheManager(redisCacheManager));
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setSessionManager(sessionManager(redisTemplate));
        return securityManager;
    }

    /**
     * 缓存管理器的配置
     *
     * @param redisCacheManager
     * @return
     */
    @Bean
    public ShiroRedisCacheManager shiroRedisCacheManager(RedisCacheManager redisCacheManager) {
        ShiroRedisCacheManager cacheManager = new ShiroRedisCacheManager();
        cacheManager.setCacheManager(redisCacheManager);
        //name是key的前缀，可以设置任何值，无影响，可以设置带项目特色的值
        return cacheManager;
    }

    /**
     * shiroFilter
     *
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);//注入securityManager
        shiroFilterFactoryBean.setLoginUrl("/login");// loginUrl认证提交地址，如果没有认证将会请求此地址进行认证
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //以下是过滤链，按顺序过滤，所以/**需要放最后
        //开放的静态资源
        filterChainDefinitionMap.put("/favicon.ico", "anon");//网站图标
        filterChainDefinitionMap.put("/login", "anon");//登录设置所有人访问
        filterChainDefinitionMap.put("/**", "user");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 自定义realm
     * 这里注入redisTemplate
     *
     * @return
     */
    @Bean
    public CustomRealm customRealm(RedisCacheManager redisCacheManager) {
        CustomRealm realm = new CustomRealm();
        realm.setCachingEnabled(true);
        //设置认证密码算法及迭代复杂度
//        realm.setCredentialsMatcher(credentialsMatcher());
        //认证
        realm.setCacheManager(shiroRedisCacheManager(redisCacheManager));
        realm.setAuthenticationCachingEnabled(true);
        //授权
        realm.setAuthorizationCachingEnabled(true);
        return realm;
    }


    /**
     * realm的认证算法
     * @return
     */
//  @Bean(name = "hashedCredentialsMatcher")
//  public HashedCredentialsMatcher credentialsMatcher() {
//    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("md5");
//    //2次迭代
//    credentialsMatcher.setHashIterations(2);
//    credentialsMatcher.setStoredCredentialsHexEncoded(true);
//    return credentialsMatcher;
//  }


    /**
     * 保证实现了Shiro内部lifecycle函数的bean执行 也是shiro的生命周期
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**
     * cookie对象;
     * rememberMeCookie()方法是设置Cookie的生成模版，比如cookie的name，cookie的有效时间等等。
     *
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        //System.out.println("ShiroConfiguration.rememberMeCookie()");
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }


    /**
     * cookie管理对象;
     * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        //System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
        return cookieRememberMeManager;
    }

    /**
     * 配置sessionmanager，由redis存储数据
     */
    @Bean(name = "sessionManager")
    @DependsOn(value = "lifecycleBeanPostProcessor")
    public DefaultWebSessionManager sessionManager(RedisTemplate redisTemplate) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        CollectionSerializer<Serializable> collectionSerializer = CollectionSerializer.getInstance();
        redisTemplate.setDefaultSerializer(collectionSerializer);
        //redisTemplate默认采用的其实是valueSerializer，就算是采用其他ops也一样，这是一个坑。
        redisTemplate.setValueSerializer(collectionSerializer);
        ShiroRedisSessionDao redisSessionDao = new ShiroRedisSessionDao(redisTemplate);
        //这个name的作用也不大，只是有特色的cookie的名称。
        sessionManager.setSessionDAO(redisSessionDao);
        sessionManager.setDeleteInvalidSessions(true);
        SimpleCookie cookie = new SimpleCookie();
        cookie.setName("starrkCookie");
        sessionManager.setSessionIdCookie(cookie);
        sessionManager.setSessionIdCookieEnabled(true);
        return sessionManager;
    }


}
